Part 4: 模型平均的联邦学习

概述: 在本教程的第2部分中,我们使用了非常简单的联邦学习版本来训练模型。这要求每个数据所有者信任模型所有者才能看到其梯度。

说明: 在本教程中,我们将展示如何使用第3部分中的高级聚合工具来允许参数由可信的“安全工作机”聚合,然后将最终结果模型发送回模型所有者(我们)。

这样,只有安全工作机才能看到谁的模型参数来自谁。我们也许能够知道模型的哪些部分发生了更改,但是我们知道哪个工作人员(Bob或Alice)进行了哪些更改,从而创建了一层隐私。

作者:

中文版译者:


In [ ]:
import torch
import syft as sy
import copy
hook = sy.TorchHook(torch)
from torch import nn, optim

第一步: 建立数据所有者

首先,我们将创建两个数据所有者(Bob和Alice),每个数据所有者拥有少量数据。 我们还将初始化一个名为“secure_worker”的安全机器。实际上,这可以是安全的硬件(例如英特尔的SGX),也可以只是受信任的中介。


In [ ]:
# 创建一对工作机

bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
secure_worker = sy.VirtualWorker(hook, id="secure_worker")


# 玩具数据集
data = torch.tensor([[0,0],[0,1],[1,0],[1,1.]], requires_grad=True)
target = torch.tensor([[0],[0],[1],[1.]], requires_grad=True)

# 通过以下方式获取每个工作机的训练数据的指针
# 向bob和alice发送一些训练数据
bobs_data = data[0:2].send(bob)
bobs_target = target[0:2].send(bob)

alices_data = data[2:].send(alice)
alices_target = target[2:].send(alice)

第二步: 建立我们的模型

对于此示例,我们将使用简单的线性模型进行训练。 我们通常可以使用PyTorch的nn.Linear构造函数对其进行初始化。


In [ ]:
# 初始化玩具模型
model = nn.Linear(2,1)

第三步:发送模型的拷贝给Alice和Bob

接下来,我们需要将当前模型的副本发送给Alice和Bob,以便他们可以对自己的数据集执行学习步骤。


In [ ]:
bobs_model = model.copy().send(bob)
alices_model = model.copy().send(alice)

bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)

In [ ]:

第4步:训练鲍勃和爱丽丝的模型(并行)

与通过安全平均进行联邦学习的常规做法一样,每个数据所有者首先在本地对模型进行几次迭代训练,然后再对模型进行平均。


In [ ]:
for i in range(10):

    # 训练Bob的模型
    bobs_opt.zero_grad()
    bobs_pred = bobs_model(bobs_data)
    bobs_loss = ((bobs_pred - bobs_target)**2).sum()
    bobs_loss.backward()

    bobs_opt.step()
    bobs_loss = bobs_loss.get().data

    # 训练Alice的模型
    alices_opt.zero_grad()
    alices_pred = alices_model(alices_data)
    alices_loss = ((alices_pred - alices_target)**2).sum()
    alices_loss.backward()

    alices_opt.step()
    alices_loss = alices_loss.get().data
    
    print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))

第5步:将两个更新的模型发送到安全工作机

现在,每个数据所有者都拥有部分受过训练的模型,是时候以安全的方式将它们平均在一起了。我们通过指示Alice和Bob将其模型发送到安全(可信)服务器来实现这一目标。

请注意,这种使用我们的API的方式意味着每个模型都直接发送到secure_worker。我们从未见过。


In [ ]:
alices_model.move(secure_worker)

In [ ]:
bobs_model.move(secure_worker)

第6步:模型平均

最后,此训练epoch(译者注:一个epoch表示全部训练数据完整训练一轮)的最后一步是将Bob和Alice的训练模型平均在一起,然后使用它来设置全局“模型”的值。


In [ ]:
with torch.no_grad():
    model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
    model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())

冲洗并重复

现在,我们只需要对此进行多次迭代!


In [ ]:
iterations = 10
worker_iters = 5

for a_iter in range(iterations):
    
    bobs_model = model.copy().send(bob)
    alices_model = model.copy().send(alice)

    bobs_opt = optim.SGD(params=bobs_model.parameters(),lr=0.1)
    alices_opt = optim.SGD(params=alices_model.parameters(),lr=0.1)

    for wi in range(worker_iters):

        # 训练Bob的模型
        bobs_opt.zero_grad()
        bobs_pred = bobs_model(bobs_data)
        bobs_loss = ((bobs_pred - bobs_target)**2).sum()
        bobs_loss.backward()

        bobs_opt.step()
        bobs_loss = bobs_loss.get().data

        # 训练Alice的模型
        alices_opt.zero_grad()
        alices_pred = alices_model(alices_data)
        alices_loss = ((alices_pred - alices_target)**2).sum()
        alices_loss.backward()

        alices_opt.step()
        alices_loss = alices_loss.get().data
    
    alices_model.move(secure_worker)
    bobs_model.move(secure_worker)
    with torch.no_grad():
        model.weight.set_(((alices_model.weight.data + bobs_model.weight.data) / 2).get())
        model.bias.set_(((alices_model.bias.data + bobs_model.bias.data) / 2).get())
    
    print("Bob:" + str(bobs_loss) + " Alice:" + str(alices_loss))

最后,我们想确保我们得到的模型学习正确,因此我们将在测试数据集上对其进行评估。在这个玩具问题中,我们将使用原始数据,但在实践中,我们将希望使用新数据来了解模型对看不见的样本的泛化程度。


In [ ]:
preds = model(data)
loss = ((preds - target) ** 2).sum()

In [ ]:
print(preds)
print(target)
print(loss.data)

在这个玩具示例中,平均模型相对于本地训练的纯文本模型表现不佳,但是我们能够在不暴露每个工人的训练数据的情况下对其进行训练。我们还能够在可信任的聚合器上聚合每个工作人员的更新模型,以防止数据泄露给模型所有者。

在未来的教程中,我们的目标是直接使用梯度进行可信聚合,以便我们可以使用更好的梯度估计来更新模型并获得更强大的模型。


In [ ]:

恭喜!!! 是时候加入社区了!

祝贺您完成本笔记本教程! 如果您喜欢此方法,并希望加入保护隐私、去中心化AI和AI供应链(数据)所有权的运动,则可以通过以下方式做到这一点!

给 PySyft 加星

帮助我们的社区的最简单方法是仅通过给GitHub存储库加注星标! 这有助于提高人们对我们正在构建的出色工具的认识。

选择我们的教程

我们编写了非常不错的教程,以更好地了解联合学习和隐私保护学习的外观,以及我们如何为实现这一目标添砖加瓦。

加入我们的 Slack!

保持最新进展的最佳方法是加入我们的社区! 您可以通过填写以下表格来做到这一点http://slack.openmined.org

加入代码项目!

对我们的社区做出贡献的最好方法是成为代码贡献者! 您随时可以转到PySyft GitHub的Issue页面并过滤“projects”。这将向您显示所有概述,选择您可以加入的项目!如果您不想加入项目,但是想做一些编码,则还可以通过搜索标记为“good first issue”的GitHub问题来寻找更多的“一次性”微型项目。

捐赠

如果您没有时间为我们的代码库做贡献,但仍想提供支持,那么您也可以成为Open Collective的支持者。所有捐款都将用于我们的网络托管和其他社区支出,例如黑客马拉松和聚会!

OpenMined's Open Collective Page


In [ ]: